#include<iostream>
#include<fstream>
#include<string>
#include <sstream>
#include <vector>
#include <queue>
#include <map>

#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <pthread.h>

#include "Logger.h"
#include "NetworkFw.h"
#include "SimVss.h"
#include "FileManager.h"
using namespace std;

Logger logger("log/vss.log");

void VssNode::StartStream() {
    networker.InitConn(TNMC_CDN_SERVER_IP_ADDR, TNMC_CDN_SERVER_TCP_PORT);  
}

void VssNode::HandleConnedMsg(FwAppMsg *msg) {
    VssSession *session = static_cast<VssSession *>(msg->sock_node->data); 

    if (session->state == VSS_SESSION_STATE_UNCONN) {
        CalcRange(session);
        RequestTransfer(session);
    }
    
    AppNode::HandleConnedMsg(msg);
}

void VssNode::HandleRecvMsg(FwAppMsg *msg) {
    VssSession *session = static_cast<VssSession *>(msg->sock_node->data); 

    while (true) {
        if (session->state == VSS_SESSION_STATE_LOCATED) {
            networker.PeekBlock(session->sockfd, text_buf, 512);
            char *needle = strstr(text_buf, "\r\n\r\n");
            if (needle) {
                networker.ReadBlock(session->sockfd, text_buf, needle - text_buf + 2);
                session->state = VSS_SESSION_STATE_PARSING_CHUNKSIZE;
            } else {
                break;
            }
        } else if (session->state == VSS_SESSION_STATE_PARSING_CHUNKSIZE) {
            networker.PeekBlock(session->sockfd, text_buf, 16);
            int field_size = GetHttpChunkLen(text_buf, session->chunklen);
            if (field_size) {// chunk-size parsed successfully
                networker.ReadBlock(session->sockfd, text_buf, field_size);
                if (session->chunklen == 0) { //end of http text
                    session->curr_pos = session->range_end + 1;
                    CalcRange(session);
                    if (session->state == VSS_SESSION_STATE_FINISHED) {
                        throw TNMC_FW_EVENT_CODE_COMPLETED;
                    }
                    RequestTransfer(session);
                } else {
                    session->state = VSS_SESSION_STATE_EXTRACTING;
                }
            } else {
                break;
            }
        }  else if (session->state == VSS_SESSION_STATE_EXTRACTING) {
            int conti = true;
            while (session->chunklen >= TNMC_HELPER_BUFFER_LEN) {
                int res = networker.ReadBlock(session->sockfd, text_buf, TNMC_HELPER_BUFFER_LEN);
                session->chunklen -= res;
                if (res < TNMC_HELPER_BUFFER_LEN) {
                    conti = false;
                    break;
                }
            }
            if (!conti) break;
            if (session->chunklen > 0) {
                int res = networker.ReadBlock(session->sockfd, text_buf, session->chunklen);
                if (res < session->chunklen) {
                    session->chunklen -= res;
                    break;
                } else {
                    session->chunklen = 0;
                }
            }

            assert(session->chunklen == 0);
            session->state = VSS_SESSION_STATE_PARSING_CHUNKSIZE;
        }
    } /*end of while*/

    delete msg;
}

void VssNode::CalcRange(VssSession *session) {
    if (session->curr_pos > session->end_pos) {
        session->state = VSS_SESSION_STATE_FINISHED;
    } else {
        session->state = VSS_SESSION_STATE_LOCATED;
        session->range_end = session->curr_pos + 1316 * 1316 - 1;
        if (session->range_end > session->end_pos) {
            session->range_end = session->end_pos;
        }
    }
}

void VssNode::RequestTransfer(VssSession *session) {
    stringstream req_stream;
    req_stream <<
        "GET /nothing HTTP/1.1\r\n"
        "User-Agent: VSS User-Agent/1.0\r\n"
        "Host: 192.168.18.190\r\n"
        "Range: bytes=" << session->curr_pos << "-" << session->range_end << "\r\n"
        "Ingress-Capacity: 3750000\r\n"
        "Content-Length: 0\r\n\r\n";

    networker.SendString(session->sockfd, req_stream.str());
}


int main(int argc, char *argv[])
{
    VssNode *app = new VssNode;

    pthread_t precvid;
    int result;
    if ((result = pthread_create(&precvid,NULL, (void* (*)(void*))process_do_networking, app)) == -1) {
        logger.log("VSS thread creation failed", LOG_LEVEL_FATAL);
        return -1;
    }
    
    app->StartStream();
    usleep(500000);
    app->StartStream();
    usleep(500000);
    app->StartStream();
    process_do_app(app);


    return 0;
}

